package com.atguigu.gmall.order.service.impl;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.ware.entity.Sku;
import com.atguigu.gmall.ware.entity.WareStockResultMsg;
import com.atguigu.gmall.ware.vo.WareSkuMapVo;
import com.google.common.collect.Lists;

import java.util.*;
import java.math.BigDecimal;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.common.auth.UserAuthInfo;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.utils.UserAuthUtils;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.user.UserInfoFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.order.dto.OrderSubmitDto;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.entity.PaymentInfo;
import com.atguigu.gmall.order.handler.manager.OrderHandlerManager;
import com.atguigu.gmall.order.mapper.OrderStatusLogMapper;
import com.atguigu.gmall.order.mapper.PaymentInfoMapper;
import com.atguigu.gmall.order.vo.DetailVo;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.atguigu.gmall.ware.entity.WareStockMsg;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.sun.org.apache.xalan.internal.xslt.Process;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author Administrator
* @description 针对表【order_info(订单表 订单表)】的数据库操作Service实现
* @createDate 2023-11-20 14:06:25
*/
@Service
@Slf4j
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService{

    @Autowired
    private UserInfoFeignClient userInfoFeignClient ;

    @Autowired
    private CartFeignClient cartFeignClient ;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor ;

    @Autowired
    private RedisTemplate<String , String> redisTemplate ;

    @Autowired
    private WareFeignClient wareFeignClient ;

    @Autowired
    private PaymentInfoMapper paymentInfoMapper ;

    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper ;

    @Autowired
    private RabbitTemplate rabbitTemplate ;

    @Autowired
    private OrderDetailMapper orderDetailMapper ;

    @Override
    public OrderConfirmVo trade() {

        // 把查询到的数据封装到OrderConfirmVo对象，并进行返回
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo() ;

        // 远程调用service-user微服务的接口，根据用户id查询收件人的地址列表
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> cf1 = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            Result<List<UserAddress>> listResult = userInfoFeignClient.findByUserId();
            List<UserAddress> addressList = listResult.getData();
            orderConfirmVo.setUserAddressList(addressList);
        }, threadPoolExecutor);

        // 远程调用service-cart微服务的接口，根据用户的id查询购物车中选中的购物项数据
        CompletableFuture<List<DetailVo>> cf2 = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            Result<List<CartItem>> result = cartFeignClient.findByUserId();
            List<CartItem> cartItemList = result.getData();
            List<DetailVo> detailVoList = cartItemList.stream().map(cartItem -> {
                DetailVo detailVo = new DetailVo();
                detailVo.setImgUrl(cartItem.getImgUrl());
                detailVo.setSkuName(cartItem.getSkuName());
                detailVo.setOrderPrice(cartItem.getSkuPrice());
                detailVo.setSkuNum(cartItem.getSkuNum());
                detailVo.setSkuId(cartItem.getSkuId());
                String hasStock = wareFeignClient.hasStock(cartItem.getSkuId(), cartItem.getSkuNum());
                detailVo.setHasStock(hasStock);
                return detailVo;
            }).collect(Collectors.toList());
            orderConfirmVo.setDetailArrayList(detailVoList);
            return detailVoList;
        }, threadPoolExecutor);

        // 总购买数量
        CompletableFuture<Void> cf3 = cf2.thenAcceptAsync(detailVoList -> {

            Integer totalNum = detailVoList.stream().map(detailVo -> detailVo.getSkuNum()).reduce((o1, o2) -> o1 + o2).get();
            BigDecimal totalAmount = detailVoList.stream().map(detailVo -> detailVo.getOrderPrice().multiply(new BigDecimal(detailVo.getSkuNum())))
                    .reduce((o1, o2) -> o1.add(o2)).get();
            orderConfirmVo.setTotalNum(totalNum);
            orderConfirmVo.setTotalAmount(totalAmount);

            // 生成交易号，并且把交易号进行返回
            String tradeNo = UUID.randomUUID().toString().replace("-", "");
            orderConfirmVo.setTradeNo(tradeNo);

            // 把交易号保存到Redis中
            redisTemplate.opsForValue().set("order:info:tradeno:" + tradeNo , "1" , 5 , TimeUnit.MINUTES);

        }, threadPoolExecutor);

        // 多任务的组合
        CompletableFuture.allOf(cf1 , cf2 , cf3).join() ;

        // 返回
        return orderConfirmVo;
    }

    @Autowired
    private OrderHandlerManager orderHandlerManager ;

    @Override
    public String submitOrder(String tradeNo, OrderSubmitDto orderSubmitDto) {
        return orderHandlerManager.exec(orderSubmitDto , tradeNo) ;
    }

    /**
     * 问题说明：在判断完毕以后更改订单状态之前，用户支付成功了，那么此时订单的状态就是已支付，在执行修改订单状态的代码，就会把订单的状态改为已关闭，这是不合理的。
     * 解决方案：
     * 1、分布式锁(悲观锁)   -----> 并发性能较弱
     * 2、乐观锁   -----> 并发性能较强
     */
    // 关闭订单
    public void closeOrder(Long orderId, Long userId) {

        // int a = 1 / 0 ;         // 模拟异常

        // 根据orderId以及userId查询订单数据
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderInfo::getId , orderId) ;
        lambdaQueryWrapper.eq(OrderInfo::getUserId , userId) ;
        OrderInfo orderInfo = this.getOne(lambdaQueryWrapper);

        if(OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus()) && ProcessStatus.UNPAID.name().equals(orderInfo.getProcessStatus())) {

            // 把订单的状态设置为已关闭
            orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
            orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
            lambdaQueryWrapper.eq(OrderInfo::getOrderStatus , OrderStatus.UNPAID.name()) ;
            lambdaQueryWrapper.eq(OrderInfo::getProcessStatus , ProcessStatus.UNPAID.name()) ;
            // update order_info set order_status = 'CLOSE' and process_status = 'CLOSE' where user_id = ? and id = ? and order_status = 'UNPAID' and process_status = 'UNPAID'
            this.update(orderInfo , lambdaQueryWrapper) ;

            // 保存订单的状态的日志数据
            saveOrderStatusLog(orderInfo);

        }

    }

    @Override
    public OrderInfo findByOrderId(String orderId) {
        UserAuthInfo userAuthInfo = UserAuthUtils.getUserAuthInfo();
        String userId = userAuthInfo.getUserId();
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderInfo::getId , Long.parseLong(orderId)) ;
        lambdaQueryWrapper.eq(OrderInfo::getUserId , Long.parseLong(userId)) ;
        OrderInfo orderInfo = this.getOne(lambdaQueryWrapper);
        return orderInfo;
    }

    @Override
    public void orderPayedUpateOrderStatus(Map<String, String> map) {

        // 保存支付信息数据(payment_info)
        PaymentInfo paymentInfo = savePaymentInfo(map) ;

        // 更改订单的状态(order_info)
        OrderInfo orderInfo = updateOrderStatusPayed(paymentInfo) ;

        // 保存状态日志数据(order_status_log)
        saveOrderStatusLog(orderInfo) ;

        // 进行库存的扣减
        WareStockMsg wareStockMsg = buildWareStockMsg(orderInfo) ;
        rabbitTemplate.convertAndSend("exchange.direct.ware.stock" , "ware.stock" , JSON.toJSONString(wareStockMsg));

    }

    @Override
    public void skuWareStockDeductUpateOrderStatus(String msg) {

        // 解析数据
        WareStockResultMsg wareStockResultMsg = JSON.parseObject(msg, WareStockResultMsg.class);
        Long orderId = wareStockResultMsg.getOrderId();
        String status = wareStockResultMsg.getStatus();

        // 根据订单的id查询订单数据
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderInfo::getId , orderId) ;
        OrderInfo orderInfo = this.getOne(lambdaQueryWrapper);

        // 判断订单的状态
        if(OrderStatus.PAID.name().equals(orderInfo.getOrderStatus()) && ProcessStatus.PAID.name().equals(orderInfo.getProcessStatus())) {

            // 修改订单状态
            OrderStatus orderStatus = null ;
            ProcessStatus processStatus = null ;
            switch (status) {
                case "DEDUCTED":
                    orderStatus = OrderStatus.WAITING_DELEVER ;
                    processStatus = ProcessStatus.NOTIFIED_WARE ;
                    break;
                case "OUT_OF_STOCK":
                    orderStatus = OrderStatus.WAITING_SCHEDULE ;
                    processStatus = ProcessStatus.STOCK_EXCEPTION ;
                    break;
            }

            orderInfo.setOrderStatus(orderStatus.name());
            orderInfo.setProcessStatus(processStatus.name());

            // 执行更新操作
            lambdaQueryWrapper.eq(OrderInfo::getOrderStatus , OrderStatus.PAID.name()) ;
            lambdaQueryWrapper.eq(OrderInfo::getProcessStatus , ProcessStatus.PAID.name()) ;
            lambdaQueryWrapper.eq(OrderInfo::getUserId , orderInfo.getUserId()) ;
            this.update(orderInfo , lambdaQueryWrapper) ;

            // 保存订单状态的日志数据
            saveOrderStatusLog(orderInfo);

        }

    }

    @Override
    public List<WareStockMsg> orderSplit(Long orderId, String wareSkuMap) {  // [{"wareId":"1","skuIds":["50","49"]},{"wareId":"2","skuIds":["42"]}]

        // 解析wareSkuMap的json数据
        List<WareSkuMapVo> wareSkuMapVos = JSON.parseArray(wareSkuMap, WareSkuMapVo.class);

        // 根据订单的id查询老订单数据
        OrderInfo oldOrderInfo = this.getById(orderId);

        // 遍历list集合，list集合中的每一个元素就是一个新的订单
        List<WareStockMsg> wareStockMsgList = new ArrayList<>() ;
        for(WareSkuMapVo wareSkuMapVo : wareSkuMapVos) {

            // 创建新的订单对象，封装要保存的订单数据
            OrderInfo orderInfo = new OrderInfo() ;
            orderInfo.setConsignee(oldOrderInfo.getConsignee());
            orderInfo.setConsigneeTel(oldOrderInfo.getConsigneeTel());

            // 获取新订单的明细数据
            List<Long> skuIds = wareSkuMapVo.getSkuIds();
            LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>() ;
            orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId , orderId) ;
            orderDetailLambdaQueryWrapper.eq(OrderDetail::getUserId , oldOrderInfo.getUserId()) ;
            orderDetailLambdaQueryWrapper.in(OrderDetail::getSkuId , skuIds) ;
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailLambdaQueryWrapper);
            BigDecimal totalAmount = orderDetails.stream().map(orderDetail -> {
                return orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum())) ;
            }).reduce((o1, o2) -> o1.add(o2)).get();
            orderInfo.setTotalAmount(totalAmount);

            orderInfo.setOrderStatus(oldOrderInfo.getOrderStatus());
            orderInfo.setUserId(oldOrderInfo.getUserId());
            orderInfo.setPaymentWay(oldOrderInfo.getPaymentWay());
            orderInfo.setDeliveryAddress(oldOrderInfo.getDeliveryAddress());
            orderInfo.setOrderComment(oldOrderInfo.getOrderComment());
            orderInfo.setOutTradeNo(oldOrderInfo.getOutTradeNo());
            OrderDetail orderDetail = orderDetails.get(0);
            orderInfo.setTradeBody(orderDetail.getSkuName());
            orderInfo.setCreateTime(new Date());
            orderInfo.setExpireTime(oldOrderInfo.getExpireTime());
            orderInfo.setProcessStatus(oldOrderInfo.getProcessStatus());
            orderInfo.setParentOrderId(oldOrderInfo.getId());
            orderInfo.setImgUrl(orderDetail.getImgUrl());
            orderInfo.setOperateTime(new Date());
            orderInfo.setOriginalTotalAmount(totalAmount);

            // 保存新订单
            this.save(orderInfo) ;

            // 保存新订单明细数据
            orderDetails.stream().map(od -> {
                OrderDetail newOrderDetail = new OrderDetail();
                BeanUtils.copyProperties(od, newOrderDetail);
                newOrderDetail.setId(null);
                newOrderDetail.setOrderId(orderInfo.getId());
                return newOrderDetail;
            }).forEach(e -> orderDetailMapper.insert(e));

            // 构建响应结果
            WareStockMsg wareStockMsg = new WareStockMsg() ;
            wareStockMsg.setOrderId(orderInfo.getId());
            wareStockMsg.setConsignee(orderInfo.getConsignee());
            wareStockMsg.setConsigneeTel(orderInfo.getConsigneeTel());
            wareStockMsg.setOrderComment(orderInfo.getOrderComment());
            wareStockMsg.setOrderBody(orderInfo.getTradeBody());
            wareStockMsg.setDeliveryAddress(orderInfo.getDeliveryAddress());
            wareStockMsg.setPaymentWay("2");
            List<Sku> skuList = orderDetails.stream().map(o -> {
                Sku sku = new Sku();
                sku.setSkuId(o.getSkuId());
                sku.setSkuNum(Integer.parseInt(o.getSkuNum()));
                sku.setSkuName(o.getSkuName());
                return sku;
            }).collect(Collectors.toList());
            wareStockMsg.setDetails(skuList);
            wareStockMsg.setWareId(wareSkuMapVo.getWareId());
            wareStockMsgList.add(wareStockMsg) ;

        }

        // 把之前的订单的状态改为已拆单
        if(OrderStatus.PAID.name().equals(oldOrderInfo.getOrderStatus()) && ProcessStatus.PAID.name().equals(oldOrderInfo.getProcessStatus())) {

            oldOrderInfo.setOrderStatus(OrderStatus.SPLIT.name());
            oldOrderInfo.setProcessStatus(ProcessStatus.SPLIT.name());

            LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
            lambdaQueryWrapper.eq(OrderInfo::getId , oldOrderInfo.getId()) ;
            lambdaQueryWrapper.eq(OrderInfo::getUserId , oldOrderInfo.getUserId()) ;
            lambdaQueryWrapper.eq(OrderInfo::getOrderStatus , OrderStatus.PAID.name()) ;
            lambdaQueryWrapper.eq(OrderInfo::getProcessStatus , ProcessStatus.PAID.name()) ;

            this.update(oldOrderInfo , lambdaQueryWrapper) ;

        }

        return wareStockMsgList;
    }

    @Override
    public String saveOrderInfo(OrderInfo orderInfo) {

        // 保存订单数据
        this.save(orderInfo) ;

        // 保存订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        orderDetailList.stream().map(orderDetail -> {
            orderDetail.setOrderId(orderInfo.getId());
            return orderDetail ;
        }).forEach( od -> this.orderDetailMapper.insert(od) );

        return String.valueOf(orderInfo.getId());
    }

    // 构建扣减库存消息
    private WareStockMsg buildWareStockMsg(OrderInfo orderInfo) {

        // 创建WareStockMsg对象，封装要发送的扣减库存消息
        WareStockMsg wareStockMsg = new WareStockMsg() ;
        wareStockMsg.setOrderId(orderInfo.getId());
        wareStockMsg.setConsignee(orderInfo.getConsignee());
        wareStockMsg.setConsigneeTel(orderInfo.getConsigneeTel());
        wareStockMsg.setOrderComment(orderInfo.getOrderComment());
        wareStockMsg.setOrderBody(orderInfo.getTradeBody());
        wareStockMsg.setDeliveryAddress(orderInfo.getDeliveryAddress());
        wareStockMsg.setPaymentWay("2");

        // 根据订单的id和用户的id查询数据库
        Long orderId = orderInfo.getId();
        Long userId = orderInfo.getUserId();
        LambdaQueryWrapper<OrderDetail> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderDetail::getOrderId , orderId) ;
        lambdaQueryWrapper.eq(OrderDetail::getUserId , userId) ;
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(lambdaQueryWrapper);
        List<Sku> skuList = orderDetails.stream().map(orderDetail -> {
            Sku sku = new Sku();
            sku.setSkuId(orderDetail.getSkuId());
            sku.setSkuNum(Integer.parseInt(orderDetail.getSkuNum()));
            sku.setSkuName(orderDetail.getSkuName());
            return sku;
        }).collect(Collectors.toList());

        wareStockMsg.setDetails(skuList);
        return wareStockMsg ;

    }

    // 保存状态日志数据(order_status_log)
    private void saveOrderStatusLog(OrderInfo orderInfo) {

        // 创建OrderStatusLog对象封装要保存的数据
        OrderStatusLog orderStatusLog = new OrderStatusLog() ;
        orderStatusLog.setUserId(orderInfo.getUserId());
        orderStatusLog.setOrderId(orderInfo.getId());
        orderStatusLog.setOrderStatus(orderInfo.getOrderStatus());
        orderStatusLog.setOperateTime(new Date());

        // 保存
        orderStatusLogMapper.insert(orderStatusLog) ;

    }

    // 更改订单的状态
    /**
     *  关单队列
     *  支付队列
     *
     *  队列中消息执行的时候的先后顺序：
     *  1、支付队列先执行，关单队列后执行: 最终订单的状态就是已支付
     *  2、关单队列先执行，支付队列后执行：订单的状态首先会被修改为已关闭 ， 支付队列后执行也就意味着支付成功了，
     *  那么此时处理方案：
     *      1、调用支付宝开放平台的退款接口进行退款操作
     *      2、直接把订单的状态改为已支付
     */
    private OrderInfo updateOrderStatusPayed(PaymentInfo paymentInfo) {

        // 查询订单的状态
        String orderId = paymentInfo.getOrderId();
        Long userId = paymentInfo.getUserId();
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderInfo::getId , Long.parseLong(orderId)) ;
        lambdaQueryWrapper.eq(OrderInfo::getUserId , userId) ;
        OrderInfo orderInfo = this.getOne(lambdaQueryWrapper);

        // 判断
        if((OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus()) && ProcessStatus.UNPAID.name().equals(orderInfo.getProcessStatus()))
                || (OrderStatus.CLOSED.name().equals(orderInfo.getOrderStatus()) && ProcessStatus.CLOSED.name().equals(orderInfo.getProcessStatus()))
        ) {

            // 把订单的状态该为已支付
            orderInfo.setOrderStatus(OrderStatus.PAID.name());
            orderInfo.setProcessStatus(ProcessStatus.PAID.name());
            lambdaQueryWrapper.in(OrderInfo::getOrderStatus , OrderStatus.UNPAID.name() , OrderStatus.CLOSED.name()) ;
            lambdaQueryWrapper.in(OrderInfo::getProcessStatus , ProcessStatus.UNPAID.name() , ProcessStatus.CLOSED.name()) ;
            this.update(orderInfo , lambdaQueryWrapper) ;

        }

        return orderInfo ;
    }

    // 保存支付信息数据
    private PaymentInfo savePaymentInfo(Map<String, String> map) {

        // 获取外部交易号
        String outTradeNo = map.get("out_trade_no");
        String tradeNo = map.get("trade_no");
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        lambdaQueryWrapper.eq(OrderInfo::getOutTradeNo , outTradeNo) ;
        OrderInfo orderInfo = this.getOne(lambdaQueryWrapper);          // 根据外部交易号查询订单数据

        // 创建PaymentInfo对象封装要保存数据
        PaymentInfo paymentInfo = new PaymentInfo() ;
        paymentInfo.setUserId(orderInfo.getUserId());
        paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
        paymentInfo.setOrderId(String.valueOf(orderInfo.getId()));
        paymentInfo.setPaymentType("ALIPAY");
        paymentInfo.setTradeNo(tradeNo);
        paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
        paymentInfo.setSubject(orderInfo.getTradeBody());
        paymentInfo.setPaymentStatus(OrderStatus.PAID.name());
        paymentInfo.setCreateTime(new Date());
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(JSON.toJSONString(map));

        // 保存数据
        paymentInfoMapper.insert(paymentInfo) ;

        return paymentInfo ;

    }

}




